package weiyao.xinxidasai.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import weiyao.xinxidasai.DTO.VerificationReport;
import weiyao.xinxidasai.DTO.versioncreate;
import weiyao.xinxidasai.Utils.Encryption.Chaos.NotaCryptor;
import weiyao.xinxidasai.Utils.ThreadLocal.UserHolder;
import weiyao.xinxidasai.mapper.NotaFilesMapper;
import weiyao.xinxidasai.mapper.UserMapper;
import weiyao.xinxidasai.mapper.VersionMapper;
import weiyao.xinxidasai.pojo.NotaFiles;
import weiyao.xinxidasai.pojo.Users;
import weiyao.xinxidasai.pojo.Version;
import weiyao.xinxidasai.service.FileNotaryService;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;

@Slf4j
@Service
public class FileNotaryServiceImpl implements FileNotaryService {

    private final versioncreate versioncreate;
    private final NotaFilesMapper notaFilesMapper;
    private final UserMapper userMapper;
    private final VersionMapper versionMapper;

    @Autowired
    public FileNotaryServiceImpl(versioncreate versioncreate,
                                 NotaFilesMapper notaFilesMapper, UserMapper userMapper, VersionMapper versionMapper) {
        this.versioncreate = versioncreate;
        this.notaFilesMapper = notaFilesMapper;
        this.userMapper = userMapper;
        this.versionMapper = versionMapper;
    }

    // 公证加密
    @Override
    public ResponseEntity<Resource> encryptFile(MultipartFile file) throws Exception {
        // 获取当前用户
        Long currentUserID = UserHolder.getUser().getUserId();
        Users currentUser = userMapper.findByUserId(currentUserID);

        log.info("开始加密文件，用户ID: {}", currentUserID);
        if (currentUser == null) {
            throw new SecurityException("用户未登录");
        }

        // 检查用户token
        if (currentUser.getSecretKey() == null || currentUser.getSecretKey().isEmpty()) {
            log.warn("用户未申请密钥，用户ID: {}", currentUserID);
            throw new IllegalArgumentException("请先申请密钥");
        }

        // 创建加密版本
        Version version = versioncreate.createVersion();
        log.info("创建加密版本: {}", version.getVersionId());

        // 保存版本到数据库
        int rowsAffected = versionMapper.insertandget(version);
        if (rowsAffected <= 0) {
            log.error("插入版本记录失败，用户ID: {}", currentUserID);
            throw new RuntimeException("插入版本记录失败");
        }

        // 计算原始文件哈希
        byte[] fileBytes = file.getBytes();
        String fileHash = DigestUtils.sha256Hex(fileBytes);
        log.info("原始文件哈希: {}", fileHash);

        // 执行加密
        InputStream input = new ByteArrayInputStream(fileBytes);
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        NotaCryptor.encryptWithToken(
                input, output, version, currentUser.getSecretKey());
        byte[] encryptedData = output.toByteArray();

        // 计算加密文件哈希
        String encryptedHash = DigestUtils.sha256Hex(encryptedData);
        log.info("加密文件哈希: {}", encryptedHash);

        // 保存公证记录
        NotaFiles notaFile = new NotaFiles();
        Long userId = currentUser.getUserId();
        notaFile.setUserId(userId);
        notaFile.setVersionId(version.getVersionId());
        notaFile.setEncryptedAt(new Date());
        notaFile.setOriginalHash(fileHash);
        notaFile.setEncryptedHash(encryptedHash);
        notaFile.setOriginalLength(fileBytes.length); // 保存原始文件长度

        notaFilesMapper.insertNotaFile(notaFile);
        log.info("文件公证记录已保存，文件ID: {}, 加密哈希: {}", notaFile.getFileId(), encryptedHash);

        // 在加密数据中添加文件ID（插入在头部之后）
        ByteArrayOutputStream finalOutput = new ByteArrayOutputStream();
        // 复制原始加密数据（包含头部）
        finalOutput.write(encryptedData);

        // 在文件末尾添加文件ID（8字节）
        ByteBuffer idBuffer = ByteBuffer.allocate(8);
        idBuffer.putLong(notaFile.getFileId());
        finalOutput.write(idBuffer.array());

        byte[] finalEncryptedData = finalOutput.toByteArray();
        String finalEncryptedHash = DigestUtils.sha256Hex(finalEncryptedData);
        log.info("最终加密文件哈希（含ID）: {}", finalEncryptedHash);

        // 返回加密文件
        String encryptedFilename = "encrypted_" + file.getOriginalFilename();
        Resource resource = new ByteArrayResource(finalEncryptedData);
        Users byUserId = userMapper.findByUserId(userId);
        return ResponseEntity.ok()
                .header("X-Original-Hash", "****************************************************************")          // 原始文件哈希
                .header("X-Encrypted-Hash", finalEncryptedHash)    // 加密文件哈希
                .header("X-File-Id", notaFile.getFileId().toString()) // 公证文件ID
                .header("X-Encrypted-At",
                        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
                                .format(notaFile.getEncryptedAt()))   // ISO 8601 格式时间
                .header("X-User-Id", byUserId.getUsername().toString())
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=" + encryptedFilename)
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(resource);
    }

    // 公证解密
    @Override
    public VerificationReport verifyFile(MultipartFile encryptedFile) throws Exception {
        // 获取当前用户
        Long currentUserID = UserHolder.getUser().getUserId();
        Users currentUser = userMapper.findByUserId(currentUserID);

        log.info("开始验证文件，用户ID: {}", currentUserID);
        if (currentUser == null) {
            throw new SecurityException("用户未登录");
        }

        // 检查用户token
        if (currentUser.getSecretKey() == null || currentUser.getSecretKey().isEmpty()) {
            log.warn("用户未申请密钥，用户ID: {}", currentUserID);
            throw new IllegalArgumentException("请先申请密钥");
        }

        byte[] encryptedBytes = encryptedFile.getBytes();
        Long fileId = null;
        NotaFiles notaFile = null;
        Version version = null;
        Users fileUser = null;

        try {
            // 尝试从文件末尾提取文件ID（最后8字节）
            if (encryptedBytes.length >= 8) {
                ByteBuffer buffer = ByteBuffer.wrap(
                        encryptedBytes, encryptedBytes.length - 8, 8);
                fileId = buffer.getLong();
                log.info("从文件提取的文件ID: {}", fileId);

                // 使用文件ID查找公证记录
                notaFile = notaFilesMapper.findByFileId(fileId);

                if (notaFile != null) {
                    // 获取加密时使用的版本
                    version = versionMapper.getVersionByVersionId(notaFile.getVersionId());
                    // 获取文件所属用户
                    fileUser = userMapper.findByUserId(notaFile.getUserId());
                    log.info("通过文件ID找到公证记录，用户: {}",
                            fileUser != null ? fileUser.getUsername() : "未知");
                }
            }
        } catch (Exception e) {
            log.warn("文件ID提取失败: {}", e.getMessage());
        }

        // 如果通过文件ID找不到记录，尝试使用其他方法
        if (notaFile == null) {
            log.warn("通过文件ID未找到记录，尝试使用其他方法...");

            try {
                // 尝试解析文件头获取基本信息
                byte[] header = Arrays.copyOfRange(encryptedBytes, 0, 52);
                byte[] salt = Arrays.copyOfRange(header, 0, 16);
                byte[] encryptedTokenBytes = Arrays.copyOfRange(header, 16, 48);

                // 解密token
                byte[] decryptedToken = new byte[32];
                for (int i = 0; i < 32; i++) {
                    decryptedToken[i] = (byte) (encryptedTokenBytes[i] ^ salt[i % salt.length]);
                }
                String embeddedToken = new String(decryptedToken, "UTF-8").trim();

                // 解析原始长度
                int originalLength = ((header[48] & 0xFF) << 24) |
                        ((header[49] & 0xFF) << 16) |
                        ((header[50] & 0xFF) << 8) |
                        (header[51] & 0xFF);

                log.info("从文件头解析: token={}, 原始长度={}", embeddedToken, originalLength);

                // 使用token和原始长度查找记录
                notaFile = notaFilesMapper.findByTokenAndLength(
                        currentUserID,
                        embeddedToken,
                        originalLength
                );

                if (notaFile != null) {
                    version = versionMapper.getVersionByVersionId(notaFile.getVersionId());
                    fileUser = userMapper.findByUserId(notaFile.getUserId());
                    log.info("通过token和长度找到公证记录，用户: {}",
                            fileUser != null ? fileUser.getUsername() : "未知");
                }
            } catch (Exception e) {
                log.error("文件头解析失败: {}", e.getMessage());
            }
        }

        if (notaFile == null || version == null) {
            log.warn("未找到有效的公证记录");
            throw new IllegalArgumentException("未找到公证记录");
        }

        // 执行解密
        InputStream input = new ByteArrayInputStream(encryptedBytes);
        VerificationReport report = NotaCryptor.decryptWithTokenRecovery(
                input, version, currentUser.getSecretKey());

        // 安全获取解密数据
        byte[] decryptedData = report.getDecryptedData() != null ?
                report.getDecryptedData() : new byte[0];

        // 验证原始文件哈希 - 添加空值保护
        String recoveredHash = "";
        if (decryptedData.length > 0) {
            recoveredHash = DigestUtils.sha256Hex(decryptedData);
        }

        boolean integrityValid = recoveredHash.equals(notaFile.getOriginalHash());
        log.info("恢复的原始文件哈希: {}, 匹配结果: {}", recoveredHash, integrityValid);

        // 设置文件所属用户名
        if (fileUser != null) {
            report.setUsername(fileUser.getUsername());
            log.info("设置文件所属用户: {}", fileUser.getUsername());
        }

        // 更新验证报告
        if (!integrityValid) {
            report.setValid(false);
            report.setTampered(true);
            report.setMessage(report.getMessage() + "; 文件完整性受损");
        } else {
            // 文件完整性验证通过，设置原始文件数据
            report.setOriginalFile(decryptedData);

            // 设置原始文件名
            String originalName = encryptedFile.getOriginalFilename();
            if (originalName != null && originalName.startsWith("encrypted_")) {
                originalName = originalName.substring("encrypted_".length());
            }
            report.setOriginalFileName(originalName);
            log.info("文件完整性验证通过，设置原始文件数据");
        }

        // 设置恢复文件信息（仅在文件被篡改时）
        if (report.isTampered() && decryptedData.length > 0) {
            String filename = "recovered_" + (report.getOriginalFileName() != null ?
                    report.getOriginalFileName() : "file");
            report.setRecoveredFile(Base64.getEncoder().encodeToString(decryptedData));
            report.setRecoveredFileName(filename);
            log.info("生成恢复文件: {}", filename);
        }

        return report;
    }
}